package  com.sat.common.widget

import android.content.Context

import android.content.res.TypedArray

import android.graphics.Camera

import android.graphics.Canvas

import android.graphics.Color

import android.graphics.Matrix

import android.graphics.Paint

import android.graphics.Rect

import android.media.AudioManager

import android.media.SoundPool

import android.text.StaticLayout

import android.text.TextPaint

import android.text.TextUtils

import android.util.AttributeSet

import android.util.TypedValue

import android.view.Gravity

import android.view.MotionEvent

import android.view.View

import android.view.ViewGroup

import android.widget.TextView


import androidx.annotation.Nullable

import androidx.recyclerview.widget.LinearSnapHelper

import androidx.recyclerview.widget.RecyclerView
import  com.sat.common.R
import  com.sat.common.bean.Data


class RecyclerWheelPicker(
    context: Context,
    @Nullable attrs: AttributeSet?,
    defStyle: Int
) :
    RecyclerView(context, attrs, defStyle) {
    private var mScrollEnabled = true
    private var mTextColor: Int
    private var mUnitColor: Int
    private var mDecorationColor: Int
    private var mTextSize: Float
    private var mUnitSize: Float
    private var mDecorationSize: Float
    private var mUnitText: String = ""
    var unitMarginRight = 0f
    private lateinit var mDecorationPaint: Paint
    private lateinit var mUnitTextPaint: TextPaint
    private lateinit var mDecorationRect: Rect
    private var mSoundPool: SoundPool? = null
    private var mSoundId = 0
    private var mSoundTrigger = -1
    private var mPickerSoundEnabled = true
    var isScrolling = true
        private set
    var isInitFinish =
        false // whether RecyclerView's children count is over zero
        private set
    private lateinit var mDecoration: IDecoration
    private var mAdapter: WheelAdapter? = null
    private lateinit var mLayoutManager: WheelPickerLayoutManager
    private var mListener: OnWheelScrollListener? = null
    private lateinit var mCamera: Camera
    private lateinit var mMatrix: Matrix

    interface OnWheelScrollListener {
        fun onWheelScrollChanged(
            wheelPicker: RecyclerWheelPicker?,
            isScrolling: Boolean,
            position: Int,
            data: Data?
        )
    }

    constructor(context: Context) : this(context, null) {}
    constructor(context: Context, @Nullable attrs: AttributeSet?) : this(context, attrs, 0) {}

    private fun init(context: Context) {
        initSound()
        overScrollMode = View.OVER_SCROLL_NEVER
        setHasFixedSize(true)
        mDecorationPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mUnitTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
        mDecorationRect = Rect()
        mDecoration = DefaultDecoration()
        mAdapter = WheelAdapter(context)
        super.setAdapter(mAdapter)
        mLayoutManager = WheelPickerLayoutManager(this)
        super.setLayoutManager(mLayoutManager)
        LinearSnapHelper().attachToRecyclerView(this)
        mCamera = Camera()
        mMatrix = Matrix()
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        // if on child has been attached , do not dispatch touch event
        return !isInitFinish || super.dispatchTouchEvent(ev)
    }

    override fun onScrollStateChanged(state: Int) {
        super.onScrollStateChanged(state)
        isInitFinish = mAdapter!!.itemCount == 0 || childCount > 0
        if (state == SCROLL_STATE_IDLE) {
            if (!isInitFinish) dispatchOnScrollEvent(true, NO_POSITION, null) else {
                val centerPosition: Int = mLayoutManager!!.findCenterItemPosition()
                if (centerPosition == NO_POSITION) {
                    dispatchOnScrollEvent(true, NO_POSITION, null)
                } else dispatchOnScrollEvent(
                    false,
                    centerPosition,
                    mAdapter!!.getData(centerPosition)
                )
            }
        } else dispatchOnScrollEvent(true, NO_POSITION, null)
    }

    override fun onScrolled(dx: Int, dy: Int) {
        super.onScrolled(dx, dy)
        isInitFinish = mAdapter!!.itemCount == 0 || childCount > 0
        if (dx == 0 && dy == 0) {
            if (!isInitFinish) dispatchOnScrollEvent(true, NO_POSITION, null) else {
                val centerPosition: Int = mLayoutManager.findCenterItemPosition()
                if (centerPosition == NO_POSITION) {
                    dispatchOnScrollEvent(true, NO_POSITION, null)
                } else dispatchOnScrollEvent(
                    false,
                    centerPosition,
                    mAdapter!!.getData(centerPosition)
                )
            }
        } else dispatchOnScrollEvent(true, NO_POSITION, null)
        if (mPickerSoundEnabled && Math.abs(dy) > 1 && mLayoutManager.mItemHeight > 0) {
            val currentTrigger: Int = mLayoutManager.mVerticalOffset / mLayoutManager.mItemHeight
            if (!mLayoutManager.mIsOverScroll && currentTrigger != mSoundTrigger) {
                playSound()
                mSoundTrigger = currentTrigger
            }
        }
    }

    private fun dispatchOnScrollEvent(
        isScrolling: Boolean,
        position: Int,
        data: Data?
    ) {
        this.isScrolling = isScrolling
        if (null != mListener) mListener!!.onWheelScrollChanged(
            this@RecyclerWheelPicker,
            isScrolling,
            position,
            data
        )
    }

    fun scrollTargetPositionToCenter(position: Int) {
        mLayoutManager.scrollTargetPositionToCenter(position, mAdapter!!.itemHeight)
    }

    override fun smoothScrollToPosition(position: Int) {
        if (mAdapter!!.itemCount == 0) return
        super.smoothScrollToPosition(position)
    }

    override fun setLayoutManager(layout: LayoutManager?) {}
    override fun setAdapter(adapter: Adapter<*>?) {}
    fun setOnWheelScrollListener(listener: OnWheelScrollListener?) {
        mListener = listener
    }

    fun setUnit(unitText: String) {
        if (mUnitText == unitText) return
        mUnitText = unitText
        invalidate()
    }

    fun setTextColor(textColor: Int) {
        mTextColor = textColor
    }

    fun setUnitColor(unitColor: Int) {
        mUnitColor = unitColor
    }

    fun setDecorationColor(decorationColor: Int) {
        mDecorationColor = decorationColor
    }

    fun setTextSize(textSize: Float) {
        mTextSize = textSize
    }

    fun setUnitSize(unitSize: Float) {
        mUnitSize = unitSize
    }

    fun setDecorationSize(decorationSize: Float) {
        mDecorationSize = decorationSize
    }

    fun setPickerSoundEnabled(enabled: Boolean) {
        mPickerSoundEnabled = enabled
    }

    fun setData(data: List<Data>?) {
        mAdapter!!.setData(data, mTextColor, mTextSize)
        super.setAdapter(mAdapter)
        // if there is no data, RecyclerView will disable scrolling,
        // we need manually notify the listener of the scroll status;
        if (null == data || data.size == 0) onScrolled(0, 0)
        // check the scroll border
        mLayoutManager.checkVerticalOffsetBound()
    }

    var isScrollEnabled: Boolean
        get() = mScrollEnabled
        set(scrollEnabled) {
            if (mScrollEnabled != scrollEnabled) {
                if (scrollEnabled) {
                    mScrollEnabled = scrollEnabled
                    smoothScrollBy(0, 1)
                }
                if (mLayoutManager.findCenterItemPosition() === -1) {
                    postDelayed({ mScrollEnabled = scrollEnabled }, 200)
                } else {
                    mScrollEnabled = scrollEnabled
                }
            }
        }

    fun setDecoration(mDecoration: IDecoration) {
        this.mDecoration = mDecoration
        invalidate()
    }

    override fun canScrollVertically(direction: Int): Boolean {
        return mScrollEnabled
    }

    override fun onDraw(c: Canvas) {
        super.onDraw(c)
        drawDecoration(c)
        drawUnitText(c)
    }

    private fun drawDecoration(c: Canvas) {
        if (null != mDecoration) {
            val decorationTop = (verticalSpace / 2 - mDecorationSize / 2).toInt()
            val decorationBottom = (verticalSpace / 2 + mDecorationSize / 2).toInt()
            mDecorationRect.set(-1, decorationTop, width + 1, decorationBottom)
            mDecorationPaint.setColor(mDecorationColor)
            mDecorationPaint.setStyle(Paint.Style.STROKE)
            mDecorationPaint.setStrokeWidth(0.25f)
            mDecoration.drawDecoration(this, c, mDecorationRect, mDecorationPaint)
        }
    }

    private fun drawUnitText(c: Canvas) {
        if (null != mUnitText && !TextUtils.isEmpty(mUnitText)) {
            mUnitTextPaint!!.color = mUnitColor
            mUnitTextPaint!!.textSize = mUnitSize
            val startX =
                width - unitMarginRight - paddingRight - StaticLayout.getDesiredWidth(
                    mUnitText,
                    0,
                    mUnitText!!.length,
                    mUnitTextPaint
                )
            val fontMetrics: Paint.FontMetrics = mUnitTextPaint!!.fontMetrics
            val startY: Float =
                verticalSpace / 2 + mUnitSize / 2 - fontMetrics.descent / 2
            c.drawText(mUnitText, startX, startY, mUnitTextPaint)
        }
    }

    override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
        // resize the text size if text can not be shown completely
        if (child is TextView) {
            val data = child.text.toString()
            if (child.textSize == mTextSize) {
                var finalTextSize = mTextSize
                val dataStringW = StaticLayout.getDesiredWidth(
                    data,
                    0,
                    data.length,
                    child.paint
                )
                if (horizontalSpace > 0 && dataStringW * 1.1f > horizontalSpace) {
                    finalTextSize = horizontalSpace / dataStringW / 1.1f * mTextSize
                }
                child.setTextSize(TypedValue.COMPLEX_UNIT_PX, finalTextSize)
            }
        }

        // scale child X Y
        val centerY = verticalSpace / 2
        val childCenterY = child.top + child.height / 2
        val factor = (centerY - childCenterY) * 1f / centerY
        val alphaFactor = 1 - 0.7f * Math.abs(factor)
        child.alpha = alphaFactor * alphaFactor * alphaFactor
        val scaleFactor = 1 - 0.3f * Math.abs(factor)
        child.scaleX = scaleFactor
        child.scaleY = scaleFactor

        // rotateX calculate rotate radius
        val rotateRadius = 2.0f * centerY / Math.PI.toFloat()
        val rad = (centerY - childCenterY) * 1f / rotateRadius
        val offsetZ =
            rotateRadius * (1 - Math.cos(rad.toDouble()).toFloat())
        val rotateDeg = rad * 180 / Math.PI.toFloat()
        // offset Y for item rotate
        val offsetY =
            centerY - childCenterY - rotateRadius * Math.sin(rad.toDouble()).toFloat() * 1.3f
        child.translationY = offsetY

        // for most of all devices, the childview would be rotated around X-axis perfectly by calling
        // view's setRotationX(). But, this would not work on huawei devices. so for the compatibility
        // here we resolve this compatibility-bug through another way by using Camera;

//  ViewCompat.setZ(child, -offsetZ);
//  child.setRotationX(rotateDeg);
        canvas.save()
        mCamera.save()
        mCamera.translate(0f, 0f, offsetZ)
        mCamera.rotateX(rotateDeg)
        mCamera.getMatrix(mMatrix)
        mCamera.restore()
        mMatrix.preTranslate(-child.width / 2f, -childCenterY.toFloat())
        mMatrix.postTranslate(child.width / 2f, childCenterY.toFloat())
        canvas.concat(mMatrix)
        val result = super.drawChild(canvas, child, drawingTime)
        canvas.restore()
        return result

//        // parent centerY ,item centerY
//        int centerY = (getHeight() - getPaddingTop() - getPaddingBottom()) / 2;
//        int childCenterY = child.getTop() + child.getHeight() / 2;
//        // alpha
//        float factor = (centerY - childCenterY) * 1f / centerY;
//        float currentFactor = 1 - 0.7f * Math.abs(factor);
//        child.setAlpha(currentFactor * currentFactor * currentFactor);
//
//        // rotate radius
//        float rotateRadius = 2.5f * centerY / (float) Math.PI;
//        // deg
//        float rad = (centerY - childCenterY) * 1f / rotateRadius;
//        float rotateDeg = rad * 180 / (float) Math.PI;
//        // for camera
//        float offsetZ = rotateRadius * (1 - (float) Math.cos(rad));
//        canvas.save();
//        // offset Y for item rotate
//        float offsetY = centerY - childCenterY - rotateRadius * (float) Math.sin(rad);
//        canvas.translate(0, offsetY);
//        mCamera.save();
//        mCamera.translate(0, 0, offsetZ);
//        mCamera.rotateX(rotateDeg);
//        mCamera.getMatrix(mMatrix);
//        mCamera.restore();
//        mMatrix.preTranslate(-child.getWidth() / 2, -childCenterY);
//        mMatrix.postTranslate(child.getWidth() / 2, childCenterY);
//        canvas.concat(mMatrix);
//        super.drawChild(canvas, child, drawingTime);
//        canvas.restore();
//        return true;
    }

    val verticalSpace: Int
        get() = height - paddingBottom - paddingTop

    val horizontalSpace: Int
        get() = width - paddingLeft - paddingRight

    private fun initSound() {
        mSoundPool = SoundPool(50, AudioManager.STREAM_SYSTEM, 5)
        try {
            // mSoundPool.load(getContext(), R.raw.wheelpickerkeypress, 1);
        } catch (e: Exception) {
        }
    }

    fun release() {
        mSoundPool!!.release()
    }

    private fun playSound() {
        try {
            mSoundPool!!.stop(mSoundId)
            mSoundId = mSoundPool!!.play(1, 1f, 1f, 0, 0, 1f)
        } catch (e: Exception) {
        }
    }

    private inner class WheelAdapter internal constructor(context: Context) :
        Adapter<ViewHolder>() {
        var context: Context
        var datas: List<Data>? = null
        var textColor = 0
        var textSize = 0f
        var itemHeight = 0

        fun setData(
            data: List<Data>?,
            textColor: Int,
            textSize: Float
        ) {
            datas = data
            this.textColor = textColor
            this.textSize = textSize
            itemHeight = (textSize * 1.3f).toInt()
            notifyDataSetChanged()
        }

        override fun onCreateViewHolder(
            parent: ViewGroup,
            viewType: Int
        ): WheelHolder {
            val textView = TextView(context)
            val layoutParams = ViewGroup.LayoutParams(layoutParams)
            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            layoutParams.height = itemHeight
            textView.layoutParams = layoutParams
            textView.gravity = Gravity.CENTER
            return WheelHolder(textView)
        }

        override fun onBindViewHolder(
            holder: ViewHolder,
            position: Int
        ) {
            if (null != datas) {
                val textView = holder.itemView as TextView
                textView.setTextColor(textColor)
                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
                textView.setText(datas!![position].data)
                textView.gravity = Gravity.CENTER
            }
        }

        fun getData(position: Int): Data? {
            return if (null == datas || position > datas!!.size - 1) null else datas!![position]
        }

        override fun getItemCount(): Int {
            return if (null == datas) 0 else datas!!.size
        }

        internal inner class WheelHolder(itemView: View?) :
            ViewHolder(itemView!!)

        init {
            this.context = context
        }
    }

    init {
        val density: Float = context.getResources().getDisplayMetrics().density
        val scaledDensity: Float = context.getResources().getDisplayMetrics().scaledDensity
        val mTypedArray: TypedArray =
            context.obtainStyledAttributes(attrs, R.styleable.RecyclerWheelPicker)
        mDecorationSize = mTypedArray.getDimension(
            R.styleable.RecyclerWheelPicker_rwp_decorationSize,
            35 * density
        )
        mDecorationColor =
            mTypedArray.getColor(R.styleable.RecyclerWheelPicker_rwp_decorationColor, -0xcccccd)
        mTextColor =
            mTypedArray.getColor(R.styleable.RecyclerWheelPicker_rwp_textColor, Color.BLACK)
        mTextSize = mTypedArray.getDimension(
            R.styleable.RecyclerWheelPicker_rwp_textSize,
            22 * scaledDensity
        )
        mUnitColor = mTypedArray.getColor(R.styleable.RecyclerWheelPicker_rwp_unitColor, mTextColor)
        mUnitSize =
            mTypedArray.getDimension(R.styleable.RecyclerWheelPicker_rwp_unitSize, mTextSize)
        mTypedArray.recycle()
        init(context)
    }
}
